/*
 * File:   DataBase.h
 * Author: wilq
 *
 * Created on June 6, 2012, 4:06 PM
 */

#ifndef DATABASE_H
#define	DATABASE_H

#include <vector>
#include <string>
#include "config.h"
#include "mysql_connection.h"

#include <cppconn/driver.h>
#include <cppconn/exception.h>
#include <cppconn/resultset.h>
#include <cppconn/statement.h>
#include <cppconn/prepared_statement.h>

#include "File.h"
#include "User.h"
#include "Group.h"
#include "Storage.h"
#include "Folder.h"
#include "DataBaseException.h"
#include <Log.h>
#include <boost/thread.hpp>
#include <boost/thread/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/tss.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include "PathInfo.h"
#include "IStorageSync.h"

#define LOG_EXCEPTION   CRITICAL("SQLException(%s): %s, (MySQL error code: %d, SQLState: %s)", __FUNCTION__, e.what(), e.getErrorCode(), e.getSQLStateCStr());

namespace CSDatabaseLib {

    /** Klasa umożliwiająca dostęp do danych zapisanych w bazie danych.
     */
    class DataBase {

        private:
            boost::thread_specific_ptr<uint32_t> s_lock;

            /** Klasa realizująca zadania transakcyjności
             */
            class DBTransaction {
            private:
				/** Punkt ostatniego zapisu */
                sql::Savepoint* m_savePoint; 
				/** obiekt na którym są wykonywane */
                DataBase *m_db; 
            public:
                /**
                 * Konstruktor który inicjalizuję transakcyjność
                 * @param db obiket na który będzie włączona transakcyjność
                 * @param select
                 */
                DBTransaction(DataBase *db, bool select = false) : m_db(db) {
                    FUNCTION_TRACK
                    if (NULL == m_db->s_lock.get()) {
                        m_db->m_mutex.lock();
                        m_db->s_lock.reset(new uint32_t);
                        *(m_db->s_lock) = 0;
                    }
                    (*(m_db->s_lock))++;
                    if (!select)
                        m_savePoint = m_db->con->setSavepoint();
                    else
                        m_savePoint = NULL;
                }

                /**
                 * Destruktor
                 */
                ~DBTransaction() {
                    FUNCTION_TRACK
                    if (NULL == (m_db->s_lock).get())
                        return;

                    if ((*(m_db->s_lock)) > 0)
                        (*(m_db->s_lock))--;

                    if (m_savePoint != NULL) {
                        try {
                            m_db->con->rollback(m_savePoint);
                        } catch (sql::SQLException &e) {
                            LOG_EXCEPTION
                        }
                        delete m_savePoint;
                        m_savePoint = NULL;
                    }

                    if (0 == *(m_db->s_lock)) {
                        m_db->s_lock.reset();
                        m_db->m_mutex.unlock();
                    }
                }

                /**
                 * Potwierdzenie wprowadzonych zmian
                 */
                void Commit() {
                    FUNCTION_TRACK
                    if (NULL == m_savePoint) return;

                    if (*(m_db->s_lock) > 1) {
                        m_db->con->releaseSavepoint(m_savePoint);
                    } else {
                        m_db->con->commit();
                    }

                    delete m_savePoint;
                    m_savePoint = NULL;

                    try {
                        m_savePoint = m_db->con->setSavepoint();
                    } catch (sql::SQLException &e) {
                        LOG_EXCEPTION
                    }
                }

                /**
                 * Cofnięcie wprowadzonych zmian
                 */
                void Rollback() {
                    FUNCTION_TRACK
                    if (NULL == m_savePoint) return;

                    try {
                        m_db->con->rollback(m_savePoint);
                    } catch (sql::SQLException &e) {
                        LOG_EXCEPTION
                    }
                    delete m_savePoint;
                    m_savePoint = NULL;
                    try {
                        m_savePoint = m_db->con->setSavepoint();
                    } catch (sql::SQLException &e) {
                        LOG_EXCEPTION
                    }
                }
            };
        public:
            /**
             * Konstruktor inicjalizujący połączenie z bazą danych.
             * @param ip Adres ip
             * @param userName Nazwa użytkownika
             * @param password Hasło użytkownika
             * @param schema Schemat
             */
            DataBase(const std::string &ip, const std::string &userName, const std::string &password, const std::string &schema);

            /**
             * Konstruktor kopiujący
             * @param orig orginalny obiekt który zostanie skopiowany
             */
            DataBase(const DataBase& orig);
            
            /**
             * Destruktor
             */
            virtual ~DataBase();

            /************************************************************ Used by storage ***************************************************************/

            /**
             * Sprawdza czy autoryzacja magazynu jest poprawna
             * @param name nazwa
             * @param key klucz
             * @return identyfikator magazynu, 0 - brak autoryzacji, 
             */
            int32_t StorageAuth(const std::string &name, const std::string &key);
            
            /**
             * Aktualizacja danych dla folderu w magazynie
             * @param link 
             * @param parentLink 
             * @param name nazwa
             * @param sync czy ma nastąpić synchronizacja
             * @param storageId identyfikator magazynu
             */
            /* Update folder data if exists, creating one with rights and owner ship copied from parent */
            void StorageFolderUpdate(const std::string &link, const std::string &parentLink, const std::string& name, bool sync, uint32_t storageId);
            
            /**
             * Usuwa folder z magazynu
             * @param link link identyfikujący łącze
             * @param storageId identyfikator magazynu
             */
            /* Remove folder and subfolders and files from same storage, if folder got files/folders from other storages, leave folder as pure virtual */
            void StorageFolderRemove(const std::string &link, uint32_t storageId);
            
            /**
             * Aktualizuję informację pliku w magazynie
             * @param link link
             * @param parentLink link rodzica
             * @param name nazwa
             * @param size rozmiar
             * @param lastWriteTime czas ostatniego zapisu
             * @param storageId identyfikator magazynu
             */
            /* Update file data if exists, creating one with rights and owner ship copied from parent */
            void StorageFileUpdate(const std::string &link, const std::string &parentLink, const std::string& name, int64_t size, uint64_t lastWriteTime, uint32_t storageId);
            
            /**
             * Usuwa plik z magazynu
             * @param link link do pliku
             * @param storageId identyfikator magazynu
             */
            /* Remove file if exists */
            void StorageFileRemove(const std::string &link, uint32_t storageId);

            /**
             * Dodaję użytkownika
             * @param userName nazwa użytkownika
             * @param password hasło użytkownika
             * @return identyfikator dodanego użytkownika
             */
            /*Used by others*/
            int32_t UserAdd(const std::string &userName, const std::string &password);
            
            /**
             * Zmiana hasła użytkownika
             * @param name nazwa 
             * @param newPassword nowe hasło
             * @return status operacji
             */
            int32_t UserChangePassword(std::string &name, const std::string &newPassword);
            
            /**
             * Usuwa użytkownika
             * @param id identyfikator użytkownika
             * @return status operacji
             */
            int32_t UserRemove(int32_t id);

            /**
             * Zmiana hasła dla użytkownika
             * @param id identyfikator użytkownika
             * @param newPassword nowe hasło użytkownika
             * @return status operacji
             */
            int32_t UserChangePassword(int32_t id, const std::string &newPassword);
            
            /**
             * Aktualizacja czasu ostatniego dostępu dla użytkownika
             * @param id identyfikator użytkownika
             * @return status operacji
             */
            int32_t UserUpdateLastAccessDate(int32_t id);
            
            /**
             * Sprawdza hasło dla użytkownika
             * @param userName nazwa użytkownika
             * @param password hasło użytkownika
             * @return identyfikator użytkownika
             */
            int32_t UserAuth(const std::string &userName, const std::string &password);

            /**
             * Pobiera użytkownika
             * @param userName nazwa użytkownika
             * @return Użytkownik
             */
            User UserGet(const std::string &userName);
            
            /**
             * Pobiera użytkownika
             * @param userId identyfikator użytkownika
             * @return użytkownik
             */
            User UserGet(int32_t userId);
            
            /**
             * Pobiera identyfikator użytkownika
             * @param name nazwa użytkownika
             * @return identyfikator użytkownika
             */
            int32_t UserGetId(const std::string &name);

            /**
             * Pobiera listę plików 
             * @param userId identyfikator użytkownika
             * @return 
             */
            int32_t UserGetFiles(int32_t userId);
            
            /**
             * Pobiera listę folderów dla użytkownika
             * @param userId identyfikator użytkownika
             * @return 
             */
            int32_t UserGetFolders(int32_t userId);

            /**
             * Ustawia uprawnienia dla użytkownika
             * @param id identyfikator użytkownika
             * @param rights uprawnienia użytkownika
             * @return status operacji
             */
            int32_t UserSetRights(int32_t id, int32_t rights);
            
            /**
             * Pobiera uprawnienia użytkownika
             * @param userId identyfikator użytkownika
             * @return status operacji
             */
            int32_t UserGetRights(int32_t userId);

            /**
             * Pobiera listę użytkowników
             * @return lista użytkowników
             */
            std::vector<User> UsersList();

            /**
             * Sprawdza czy użytkownik należy do grupy
             * @param userId identyfikator użytkownika
             * @param groupId identyfikator grupy
             * @return 0 - nie należy, 1 należy do grupy
             */
            int32_t UserGotGroup(int32_t userId, int32_t groupId);

            /**
             * Pobiera grupy do których należy yżytkownik
             * @param userId identyfikator użytkownika
             * @return 
             */
            std::vector<Group> UserGetGroups(int32_t userId);
            
            /**
             * Dodanie użytkownika do grupy
             * @param userId identyfikator użytkownika
             * @param groupId identyfikator grupy
             * @return status operacji
             */
            int32_t UserJoinGroup(int32_t userId, int32_t groupId);
            
            /**
             * Usuwa użytkownika z grupy
             * @param userId identyfikator użytkownika
             * @param groupId identyfikator grupy
             * @return status operacji
             */
            int32_t UserLeaveGroup(int32_t userId, int32_t groupId);

            /**
             * Sprawdza czy magazyn należy do danej grupy
             * @param userId identyfikator użytkownika
             * @param storageId identyfikator magazynu
             * @return 
             */
            int32_t UserGotStorage(int32_t userId, int32_t storageId);
            
            /**
             * Pobiera listę magazynów do którego należy użytkownik
             * @param userId identyfikator użytkownika
             * @return lista magazynów
             */
            std::vector<Storage> UserGetStorages(int32_t userId);
            
            /**
             * Dodaję użytkownika do magazynu
             * @param userId identyfikator użytkownika
             * @param storageId identyfikator magazynu
             * @return 
             */
            int32_t UserJoinStorage(int32_t userId, int32_t storageId);
            
            /**
             * Usuwa użytkownika z danego magazynu
             * @param userId identyfikator użytkownika
             * @param storageId identyfikator magazynu
             * @return 
             */
            int32_t UserLeaveStorage(int32_t userId, int32_t storageId);

            /**
             * Dodaję grupę
             * @param name nazwa grupy
             * @return identyfikator grupy
             */
            int32_t GroupAdd(const std::string &name);
            
            /**
             * Usuwa grupę
             * @param groupId identyfikator grupy
             * @return identyfikator grupy
             */
            int32_t GroupRemove(int32_t groupId);

            /**
             * Pobiera grupę
             * @param groupId identyfikator grupy
             * @return grupa
             */
            Group GroupGet(int32_t groupId);
            
            /**
             * Pobiera grupę
             * @param name nazwa grupy
             * @return grupa
             */
            Group GroupGet(const std::string &name);
            
            /**
             * Pobiera identyfikator grupy
             * @param name nazwa grupy
             * @return identyfikator grupy
             */
            int32_t GroupGetId(const std::string &name);

            /**
             * Ustawia upranienia dla grupy
             * @param id identyfikator
             * @param rights uprawninia
             * @return status operacji
             */
            int32_t GroupSetRights(int32_t id, int32_t rights);
            
            /**
             * Pobiera uprawnienia grupy
             * @param id identyfikator
             * @return uprawnienia
             */
            int32_t GroupGetRights(int32_t id);

            /**
             * Pobranie użytkowników którzy należą do grupy
             * @param groupId identyfikator grupy
             * @return lista użytkowników
             */
            std::vector<User> GroupGetUsers(int32_t groupId);
            
            /**
             * Sprawdzenie czy magazyn należy do grupy
             * @param groupId identyfikator grupy
             * @param storageId identyfikator magazynu
             * @return 
             */
            int32_t GroupGotStorage(int32_t groupId, int32_t storageId);
            
            /**
             * Pobranie magazynów które należą do grupy
             * @param groupId identyfikator grupy
             * @return lista magazynów
             */
            std::vector<Storage> GroupGetStorages(int32_t groupId);

            /**
             * Dodanie magazynu do grupy
             * @param groupId identyfikator grupy
             * @param storageId identyfikator magazynu
             * @return status operacji
             */
            int32_t GroupJoinStorage(int32_t groupId, int32_t storageId);
            
            /**
             * Usunięcie grupy z magazynu
             * @param groupId identyfikator grupy
             * @param storageId identyfikator magazynu
             * @return status operacji
             */
            int32_t GroupLeaveStorage(int32_t groupId, int32_t storageId);

            /**
             * Pobranie listy grup
             * @return lista grup
             */
            std::vector<Group> GroupList();

            /**
             * Dodanie magazynu
             * @param name nazwa 
             * @param key klucz
             * @return identyfikator dodanego magazynu
             */
            int32_t StorageAdd(const std::string &name, const std::string &key);
            
            /**
             * Usunięcie magazynu
             * @param id identyfikator magazynu
             * @return status operacji
             */
            int32_t StorageRemove(int32_t id);

            /**
             * Pobranie magazynu
             * @param storageId identyfikator magazynu
             * @return magazyn
             */
            Storage StorageGet(int32_t storageId);
            
            /**
             * Pobranie magazynu
             * @param name nazwa magazynu
             * @return magazyn
             */
            Storage StorageGet(const std::string &name);
            
            /**
             * Pobranie identyfikator magazynu
             * @param name nazwa
             * @return identyfikator magazynu
             */
            int32_t StorageGetId(const std::string &name);

            /**
             * Zmiana klucza magazynu
             * @param storage magazyn
             * @param key klucz
             * @return status operacji
             */
            int32_t StorageChangeKey(const std::string &storage, const std::string &key);
            
            /**
             * Zmiana klucza dla magazynu
             * @param storageId identyfikator magazynu
             * @param key klucz
             * @return status operacji
             */
            int32_t StorageChangeKey(int32_t storageId, const std::string &key);

            /**
             * Pobranie listy magazynów
             * @return lista magazynów
             */
            std::vector<Storage> StorageList();
            
            /**
             * Pobranie użytkowników którzy należą do magazynu
             * @param storageId identyfikator magazynu
             * @return lista użytkowników
             */
            std::vector<User> StorageGetUsers(int32_t storageId);
            
            /**
             * Pobranie grup które mają uprawnienia do magazynu
             * @param storageId identyfikator magazynu
             * @return lista grup
             */
            std::vector<Group> StorageGetGroups(int32_t storageId);

            /**
             * Stworzenie katalogu
             * @param name nazwa
             * @param parentId identyfikator rodzica
             * @param rights uprawnienia
             * @param ownerUserId identyfikator użytkownika posiadająćego katalog
             * @param ownerGroupId identyfikator grupy 
             * @param storageId identyfikator magazynu
             * @param link link
             * @param sync czy jest zsynchronizowany
             * @return identyfikator folderu
             */
            int32_t FolderCreate(const std::string &name, int32_t parentId, int32_t rights, int32_t ownerUserId, int32_t ownerGroupId, int32_t storageId, const std::string &link, bool sync = false);
            
            /**
             * Pobranie folderu
             * @param id identyfikator folderu
             * @return folder
             */
            Folder FolderGet(int32_t id);
            
            /**
             * Pobranie folderu 
             * @param name nazwa folderu
             * @return folder
             */
            Folder FolderGet(const std::string &name);
            
            /**
             * Sprawdza folder istnieje 
             * @param id identyfikator folderu
             * @return status operacji
             */
            int32_t FolderExist(int32_t id);

            /**
             * Usuwa folder 
             * @param id identyfikator folderu
             * @return status operacji
             */
            int32_t FolderRemove(int32_t id);
            
            /**
             * Przenosi folder
             * @param id identyfikator folderu
             * @param storageId identyfikator magazynu
             * @param timeStamp czas 
             * @param recursive czy pod katalogi także przenieść
             * @return status operacji
             */
            int32_t FolderRemove(int32_t id, int32_t storageId, uint64_t timeStamp = 0, bool recursive = true);
            
            /**
             * Zmiana nazwy folderu
             * @param id identyfikator folderu
             * @param newName nazwa folderu
             * @return status operacji
             */
            int32_t FolderRename(int32_t id, const std::string &newName);
            
            /**
             * Edycja folderu
             * @param id identyfikator folderu
             * @param newName nowa nazwa
             * @param rights uprawnienia 
             * @param ownerUserId identyfikator użytkownika posiadającego folder
             * @param ownerGroupId identyfikator grupy posiadającego folder
             * @param link link 
             * @param storageId identyfikator magazynu
             * @return status operacji
             */
            int32_t FolderEdit(int32_t id, const std::string &newName, int32_t rights, int32_t ownerUserId, int32_t ownerGroupId, const std::string &link, uint32_t storageId);
            
            /**
             * Pobranie identyfikatora folderu
             * @param name nazwa folderu
             * @return identyfikator folderu
             */
            int32_t FolderGetId(const std::string &name);

            /**
             * Przeniesienie folderu
             * @param folderId identyfikator folderu
             * @param newParentFolder identyfikator nowego folderu 
             * @return 
             */
            int32_t FolderMove(int32_t folderId, int32_t newParentFolder);
            
            /**
             * Kopiuję folder
             * @param folderId identyfikator folderu
             * @param newParentFolder identyfikator nowego folderu
             * @return 
             */
            int32_t FolderCopy(int32_t folderId, int32_t newParentFolder);
            
            /**
             * Pobiera scieżkę folderu
             * @param id identyfikator folderu
             * @return scieżka folderu 
             */
            std::string FolderGetPath(int32_t id);

            /**
             * Pobiera listę pod folderów
             * @param folderId identyfikator folderu
             * @return lista folderów
             */
            std::vector<Folder> FolderList(int32_t folderId);
            
            /**
             * Pobiera listę wszystkich folderów
             * @return lista folderów
             */
            std::vector<Folder> FolderListAll();

            /**
             * Tworzy plik
             * @param folderId identyfikator folderu w którym będzie plik 
             * @param name nazwa 
             * @param rights uprawnieniea
             * @param ownerUser identyfikator użytkownika posiadającego plik
             * @param ownerGroup identyfikator grupy
             * @param size rozmiar
             * @param link link
             * @param idStorage identyfikator magazynu
             * @return identyfikator stoworzonego pliku
             */
            int32_t FileCreate(int32_t folderId, const std::string &name, int32_t rights, int32_t ownerUser, int32_t ownerGroup, int32_t size, const std::string &link, int32_t idStorage);

            /**
             * Kopiuję plik 
             * @param folderId identyfikator folderu 
             * @param newParentFolder identyfikator folderu w którym będzie znajdował się plik
             * @return status operacji
             */
            int32_t FileCopy(int32_t folderId, int32_t newParentFolder);
            
            /**
             * Przeniesienie pliku
             * @param folderId identyfikator folderu
             * @param newParnetFolder identyfikator folderu w którym będzie znajdował się plik
             * @return 
             */
            int32_t FileMove(int32_t folderId, int32_t newParnetFolder);
            
            /**
             * Edycja pliku
             * @param id identyfikator pliku
             * @param newName nowa nazwa
             * @param rights uprawnienia
             * @param ownerUserId identyfikator użytkownika
             * @param ownerGroupId identyfikator grupy
             * @param link link
             * @param storageId identyfikator magazynu
             * @return status operacji
             */
            int32_t FileEdit(int32_t id, const std::string &newName, int32_t rights, int32_t ownerUserId, int32_t ownerGroupId, const std::string &link, uint32_t storageId);
            
            /**
             * Pobiera plik
             * @param id identyfikator pliku
             * @return plik
             */
            File FileGet(int32_t id);
            
            /**
             * Pobiera plik
             * @param name nazwa pliku
             * @return plik
             */
            File FileGet(const std::string &name);
            
            /**
             * Pobiera identyfikator pliku
             * @param name nazwa 
             * @return identyfikator pliku
             */
            int32_t FileGetId(const std::string &name);
            
            /**
             * Sprawdza czy plik istnieje
             * @param fileId identyfikator pliku
             * @return status operacji
             */
            int32_t FileExist(int32_t fileId);

            /**
             * Pobiera listę plików w folderze
             * @param folderId identyfikator folderu
             * @return lista plików
             */
            std::vector<File> FileList(int32_t folderId);
            
            /**
             * Pobiera listę wszystkich plików
             * @return lista plików
             */
            std::vector<File> FileListAll();

            /**
             * Usuwa plik
             * @param fileId identyfikator pliku
             * @return status operacji
             */
            int32_t FileRemove(int32_t fileId);

            /**
             * Pobiera informację o scieżce 
             * @param path scieżka
             * @param firstRoot czy jest to główny katalog
             * @return informację o scieżce
             */
            boost::shared_ptr<PathInfo> GetInfo(const std::string &path, bool firstRoot = false);
            
            /**
             * Pobiera czas według bazy danych
             * @return liczba sekund od 01.01.1970
             */
            uint64_t GetTime();

            /**
             * Sprawdz czy jest połączony z bazą danych
             * @return status
             */
            bool isConnected();

            /**
             * Ustawia intefejs 
             * @param inf
             */
            inline void SetStorageInterface(IStorageSync *inf) {
                interface = inf;
            }
        protected:
			/** Metoda zwraca identyfikator ostatniego wstawionego wiersza */
            uint64_t LastInsertId(); 
        private:
            /**
             * Szyfrowanie 
             * @param str string do zaszyfrowania
             * @return zaszyfrowany string
             */
            std::string crypt(const std::string &str);
            
            /**
             * Pobiera informację o aktualizacji
             * @param path scieżka
             * @return status
             */
            bool _GetInfoUpdate(PathInfo *path);
            /** sterownik do bazy danych */
            sql::Driver *driver; 
			/** połączenie z bazą danych */
            sql::Connection *con; 
			/** mutex służący do synchronizacji */
            boost::mutex m_mutex; 
			/** interfajs magazynu */
            IStorageSync *interface; 
        };
    };
#endif	/* DATABASE_H */

